package aaa.snow;

public class SnowFlake {

    //机房id
    private long computerRoomId;
    //机器id
    private long machineId;
    //序列号
    private long sequence = 0L;

    /**
     * 占用位数
     */

    //机房id占用5bit
    private final long computerRoomBitCnt = 5L;
    //机器id占用5bit
    private final long machineBitCnt = 5L;
    //序列号占用12bit
    private final long sequenceBitCnt = 12L;

    /**
     * 移动位数
     */

    //机器id移动位数
    private final long machineShift = sequenceBitCnt;
    //机房id移动位数
    private final long computerRoomShift = machineShift+machineBitCnt;
    //时间戳移动位数
    private final long timeStampShift = computerRoomShift+computerRoomBitCnt;

    /**
     * 允许最大
     */
    //最大机房id
    private final long maxComputerRoomId=-1^(-1<<computerRoomBitCnt);
    //最大机器id
    private final long maxMachineId=-1^(-1<<machineBitCnt);
    //序列号掩码
    private final long sequenceMask = -1^(-1<<sequenceBitCnt);

    //上一次生成的时间戳
    private long lastTimeStamp = -1L;

    public SnowFlake(long computerRoomId, long machineId) {
        if(computerRoomId<0|| computerRoomId > maxComputerRoomId){
            throw new IllegalArgumentException("超过限制");
        }
        if(machineId<0|| machineId > maxMachineId){
            throw new IllegalArgumentException("超过限制");
        }
        this.computerRoomId=computerRoomId;
        this.machineId=machineId;
    }

    public synchronized long getNextId(){
        long currentTimeStamp=System.currentTimeMillis();

        if(currentTimeStamp == lastTimeStamp){
            sequence = (sequence+1) & sequenceMask;
            //表示这个毫秒序列号用完了
            if(sequence == 0){
                currentTimeStamp=getNextMillis();
            }
        }else {
            sequence = 0;
        }

        lastTimeStamp = currentTimeStamp;

        return (currentTimeStamp<<timeStampShift)|(computerRoomId<<computerRoomShift)|(machineId<<machineShift)|sequence;
    }

    private long getNextMillis() {
        long currnentTimeStamp=System.currentTimeMillis();
        while (currnentTimeStamp<= lastTimeStamp){
            currnentTimeStamp=System.currentTimeMillis();
        }
        return currnentTimeStamp;
    }

    public static void main(String[] args) {
        SnowFlake snowFlake=new SnowFlake(1,2);
        System.out.println(snowFlake.getNextId());
    }
}
